#ifndef __SVM_H
#define __SVM_H

namespace SVM
{

struct svm_node
{
	int index;
	double value;
};

struct svm_problem
{
  int feature_dim;
	int l;
	double *y;
	struct svm_node **x;
};

enum svm_type { C_SVC, NU_SVC, ONE_CLASS, EPSILON_SVR, NU_SVR };	/* svm_type */
// added support for interesction, chi-squared, sqrt (Hellinger's) and Jensen-Shannon's kernels	
enum svm_kernel { LINEAR, POLY, RBF, SIGMOID, PRECOMPUTED, INTERSECTION, CHISQUARED, JS}; /* kernel_type */

struct svm_parameter
{
	int type;
	int kernel_type;
	int degree;	/* for poly */
	double gamma;	/* for poly/rbf/sigmoid */
	double coef0;	/* for poly/sigmoid */

	/* these are for training only */
	double cache_size; /* in MB */
	double eps;	/* stopping criteria */
	double C;	/* for C_SVC, EPSILON_SVR and NU_SVR */
	int nr_weight;		/* for C_SVC */
	int *weight_label;	/* for C_SVC */
	double* weight;		/* for C_SVC */
	double nu;	/* for NU_SVC, ONE_CLASS, and NU_SVR */
	double p;	/* for EPSILON_SVR */
	int shrinking;	/* use the shrinking heuristics */
	int probability; /* do probability estimates */

  int samples;
  int feature_dim;
  /*double *shift;
  double *scale;*/

  svm_parameter() {};

  svm_parameter(const svm_parameter &rhs) 
  {
    type = rhs.type;
    kernel_type = rhs.kernel_type;
    degree = rhs.degree;
    gamma = rhs.gamma;
    coef0 = rhs.coef0;

    cache_size = rhs.cache_size;
    eps = rhs.eps;
    C = rhs.C;
    nr_weight = rhs.nr_weight;
    weight_label = (int*)malloc(nr_weight*sizeof(int));
    weight = (double*)malloc(nr_weight*sizeof(double));
    for(int i = 0; i < nr_weight; ++i)
    {
      weight_label[i] = rhs.weight_label[i];
      weight[i] = rhs.weight[i];
    }
    nu = rhs.nu;
    p = rhs.p;
    shrinking = rhs.shrinking;
    probability = rhs.probability;

    feature_dim = rhs.feature_dim;
    samples = rhs.samples;

    /*shift = (double*)malloc(feature_dim*sizeof(double));
    for(int i = 0; i < feature_dim; ++i)
      shift[i] = rhs.shift[i];

    scale = (double*)malloc(feature_dim*sizeof(double));
    for(int i = 0; i < feature_dim; ++i)
      scale[i] = rhs.scale[i];*/
  };
};

//
// svm_model
// 
struct svm_model
{
	struct svm_parameter param;	/* parameter */
	int nr_class;		/* number of classes, = 2 in regression/one class svm */
	int l;			/* total #SV */
	struct svm_node **SV;		/* SVs (SV[l]) */
	double **sv_coef;	/* coefficients for SVs in decision functions (sv_coef[k-1][l]) */
	double *rho;		/* constants in decision functions (rho[k*(k-1)/2]) */
	double *probA;		/* pariwise probability information */
	double *probB;

	/* for classification only */

	int *label;		/* label of each class (label[k]) */
	int *nSV;		/* number of SVs for each class (nSV[k]) */
				/* nSV[0] + nSV[1] + ... + nSV[k-1] = l */
	/* XXX */
	int free_sv;		/* 1 if svm_model is created by svm_load_model*/
				/* 0 if svm_model is created by svm_train */
};

struct svm_approx_model
{
	struct svm_parameter param;	/* parameter */
	int nr_class;		/* number of classes, = 2 in regression/one class svm */
	int l;			/* total #SV */
	double *rho;		/* constants in decision functions (rho[k*(k-1)/2]) */
	double *probA;		/* pariwise probability information */
	double *probB;

	/* for classification only */

	int *label;		/* label of each class (label[k]) */
	int *nSV;		/* number of SVs for each class (nSV[k]) */
				/* nSV[0] + nSV[1] + ... + nSV[k-1] = l */

  double **fa;
  double **fx;
  double *a;
  double *b;

  svm_approx_model() {};

  svm_approx_model(const svm_model &rhs) : param(rhs.param)
  {
	  nr_class = rhs.nr_class;
	  l = rhs.l;
    int n = nr_class*(nr_class-1)/2;

    rho = (double*)malloc(n*sizeof(double));
    for(int i = 0; i < n; ++i)
      rho[i] = rhs.rho[i];

    if(rhs.label != NULL)
    {
      label = (int*)malloc(nr_class*sizeof(int));
      for(int i = 0; i < nr_class; ++i)
        label[i] = rhs.label[i];
    }
    else
      label = NULL;

	  if(rhs.probA != NULL)
	  {
		  probA = (double*)malloc(n*sizeof(double));
      for(int i = 0; i < n; ++i)
        probA[i] = rhs.probA[i];
	  }
	  else
		  probA = NULL;

	  if(rhs.probB != NULL)
	  {
		  probB = (double*)malloc(n*sizeof(double));
      for(int i = 0; i < n; ++i)
        probB[i] = rhs.probB[i];
	  }
	  else
		  probB = NULL;

	  if(rhs.nSV != NULL)
	  {
		  nSV = (int*)malloc(nr_class*sizeof(int));
      for(int i = 0; i < nr_class; ++i)
        nSV[i] = rhs.nSV[i];
	  }
	  else
		  nSV = NULL;

	  if(rhs.nSV != NULL)
	  {
		  nSV = (int*)malloc(nr_class*sizeof(int));
      for(int i = 0; i < nr_class; ++i)
        nSV[i] = rhs.nSV[i];
	  }
	  else
		  nSV = NULL;
  };
};

struct svm_model *svm_train(const struct svm_problem *prob, const struct svm_parameter *param);
void svm_cross_validation(const struct svm_problem *prob, const struct svm_parameter *param, int nr_fold, double *target);
struct svm_approx_model* svm_create_approx_model(const struct svm_model *model, const struct svm_parameter *param, const bool binary);

int svm_save_model(const char *model_file_name, const struct svm_model *model);
struct svm_model *svm_load_model(const char *model_file_name);
int svm_save_approx_model(const char *model_file_name, const struct svm_approx_model *approx_model);
struct svm_approx_model* svm_load_approx_model(const char *model_file_name);

int svm_get_svm_type(const struct svm_model *model);
int svm_get_nr_class(const struct svm_model *model);
void svm_get_labels(const struct svm_model *model, int *label);
double svm_get_svr_probability(const struct svm_model *model);
double* svm_get_single_dimension_sv(const struct svm_model *model, const int index);

double svm_predict_values(const struct svm_model *model, const struct svm_node *x, double* dec_values);
double svm_predict(const struct svm_model *model, const struct svm_node *x);
double svm_predict_probability(const struct svm_model *model, const struct svm_node *x, double* prob_estimates);
double svm_predict_approx(const struct svm_approx_model *approx_model, const struct svm_node *x);
double svm_predict_approx_probability(const struct svm_approx_model *approx_model, const struct svm_node *x, double* prob_estimates);
double svm_predict_approx_values(const struct svm_approx_model *approx_model, const struct svm_node *x, double* dec_values);

void svm_free_model_content(struct svm_model *model_ptr);
void svm_free_and_destroy_model(struct svm_model **model_ptr_ptr);
void svm_destroy_param(struct svm_parameter *param);
void svm_free_and_destroy_problem(struct svm_problem **prob);
void svm_free_and_destroy_approx_model(struct svm_approx_model **approx_model);

const char *svm_check_parameter(const struct svm_problem *prob, const struct svm_parameter *param);
int svm_check_probability_model(const struct svm_model *model);

void svm_set_print_string_function(void (*print_func)(const char *));

struct svm_problem* svm_read_problem(const char *filename);

// deprecated
// this function will be removed in future release
void svm_destroy_model(struct svm_model *model_ptr); 

};
#endif
